package com.webapp.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import com.webapp.domain.entity.BaseEntity;
import com.webapp.domain.vo.Pagination;

public class AbstractDAO<T> {
	
	 private Class<T> clazz;
	 
	 @Autowired
	 private SessionFactory sessionFactory;
	 @SuppressWarnings("unchecked")
	 public AbstractDAO() {
	        this.clazz =  (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	 }


	 public Session getSession() {
		return sessionFactory.getCurrentSession();
	 }

	 public Serializable save(Object o) {
		if(o instanceof BaseEntity) {
			BaseEntity entity = (BaseEntity) o;
            return getSession().save(entity);
		}
		return getSession().save(o);
	 }
	
	 @SuppressWarnings("unchecked")
	 public T get(Long id) {
		 return  (T) getSession().get(clazz, id);
	 }
	 @SuppressWarnings("unchecked")
	 public T load(Serializable id) {
	        return (T) getSession().load(clazz, id);
	 }
	 public void update(Object o) {
	        if (o instanceof BaseEntity) {
	        	BaseEntity entity = (BaseEntity) o;
	            getSession().update(entity);
	        } else {
	            getSession().update(o);
	        }
	 }
	  protected T get(String queryString, Object... values) {
		  Query queryObject = getSession().createQuery(from(queryString));
			if (values != null) {
				for (int i = 0; i < values.length; i++) {
					queryObject.setParameter(i, values[i]);
				}
			}
			List<T> list =  queryObject.list();
	        return list.size() > 0 ? list.get(0) : null;
	  }
	  
	  
	 @SuppressWarnings("unchecked")
	 public List<T> loadAll() {
		 Criteria criteria = getSession().createCriteria(clazz);
			criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
			return criteria.list();
	 }
	 
	 @SuppressWarnings("unchecked")
	 protected List<T> find(String queryString, Object... values) {
		 Query queryObject = getSession().createQuery(from(queryString));
			if (values != null) {
				for (int i = 0; i < values.length; i++) {
					queryObject.setParameter(i, values[i]);
				}
			}
			return queryObject.list();
	 }
	 
	 protected List<?> find(String queryString) {
		 return find(queryString, (Object[]) null);
	 }
	 
	 protected T getByCriteria(DetachedCriteria criteria) {
	        List<T> list = findByCriteria(criteria);
	        return list.size() > 0 ? list.get(0) : null;
	 }
	 
	 @SuppressWarnings("unchecked")
     public Pagination<T> paginateByCriteria(Pagination<T> p, DetachedCriteria detachedCriteria) {
	    	Criteria criteria = detachedCriteria.getExecutableCriteria(getSession());
	    	Long totalCount = ((Long) criteria.setProjection(Projections.rowCount()).uniqueResult());
	    	p.setTotalCount(totalCount);
	    	criteria.setFirstResult(p.getPosStart());
	    	criteria.setMaxResults(p.getPageSize());
	    	if (!StringUtils.isEmpty(p.getOrderBy())) {
	    		if ("desc".equals(p.getDirection())) {
	    			criteria.addOrder(Order.desc(p.getOrderBy()));
	    		} else {
	    			criteria.addOrder(Order.asc(p.getOrderBy()));
	    		}
	    	} 
	    	p.setData(criteria.list());
	    	
	    	return p;
	 }
	 
	 public Object merge(Object o) {
	        return getSession().merge(o);
	 }
	 
	 
	 @SuppressWarnings("unchecked")
	 protected List<T> findByCriteria(DetachedCriteria criteria) {
		 Criteria executableCriteria = criteria.getExecutableCriteria(getSession());
			return executableCriteria.list();
	    }
	 
	 @SuppressWarnings("unused")
	 private String from(String queryString) {
	        return "from " + clazz.getSimpleName() + " as "
	            + clazz.getSimpleName().toLowerCase() + " " + queryString;
	    }
	 

		/**
		 * 执行sql，返回long
		 * @param countHql
		 * @return
		 */
		public Long getCount(String countHql){
			Object object = getSession().createQuery(countHql).uniqueResult();
			return (Long)object;
		}
		
}
